#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ppp_ctrl.h"
#include "modem_usb.h"
#include "atchannel.h"
#include <telephony/dongle.h>
#include "sunxi_ril.h"
#include "uevent.h"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include "dongle_client.h"
#include "dongle_util.h"
#define LOG_TAG "DONGLE"
#include <utils/Log.h>

#define DONGLE_LOG 1

struct _DongleClient {
	Dongle* 								owner;
	PppCtrl* 								ppp;
	char* 									numeric;
	struct modem_info* 						modem;
	dongle_client_get_operator_numeric		get_operator_numberic;
	dongle_client_setup_data_connection 	setup_data_connection;
};

static int normal_dongle_setup_cdma_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd);
static int normal_dongle_setup_gsm_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd);
static int normal_dongle_setup_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd);
static int dm_dongle_setup_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd);
static char* dm_dongle_get_operator_numeric(DongleClient* thiz);
static char* normal_dongle_get_operator_numeric(DongleClient* thiz);
static DongleClient* dm_dongle_client_create(PppCtrl* ppp);
static DongleClient* normal_dongle_client_create(PppCtrl* ppp);

void client_set_modem_info(DongleClient* thiz, struct modem_info* modem)
{
	if(thiz != NULL)
	{
		thiz->modem = modem;
	}
}

void client_stop_pppd(DongleClient* thiz)
{
	if(thiz != NULL)
	{
		ppp_ctrl_stop_pppd(thiz->ppp);
	}
}

char* client_get_ppp_interface(DongleClient* thiz)
{
	if(thiz != NULL && thiz->ppp != NULL)
	{
		return ppp_ctrl_get_dev_name(thiz->ppp);
	}

	return NULL;
}

void client_destory(DongleClient* thiz)
{
	if(thiz != NULL)
	{
		if(thiz->numeric != NULL)
		{
			free(thiz->numeric);
			thiz->numeric = NULL;
		}
	}
}

char* client_get_dongle_name(DongleClient* thiz)
{
	if(thiz != NULL && thiz->modem != NULL)
	{
		return thiz->modem->dongle_name;
	}

	return NULL;
}

static DongleClient* normal_dongle_client_create(PppCtrl* ppp)
{
	DongleClient* thiz = calloc(1, sizeof(DongleClient));
	if(thiz != NULL)
	{
#ifdef DONGLE_LOG
		ALOGD("%s %d thiz:%p", __func__, __LINE__, thiz);
#endif
		
		thiz->get_operator_numberic = normal_dongle_get_operator_numeric;
#ifdef DONGLE_LOG
		ALOGD("%s %d get_operator_numberic:%p", __func__, __LINE__,  thiz->get_operator_numberic);
#endif
		thiz->setup_data_connection = normal_dongle_setup_data_connection;
#ifdef DONGLE_LOG
		ALOGD("%s %d setup_data_connection:%p", __func__, __LINE__,  thiz->setup_data_connection);
#endif
		
		thiz->ppp = ppp;
#ifdef DONGLE_LOG
		ALOGD("%s %d ppp:%p", __func__, __LINE__,  thiz->ppp);
#endif
	}

	return thiz;
}

char* client_get_operator_numeric(DongleClient* thiz)
{
	if(thiz != NULL && thiz->get_operator_numberic != NULL)
	{
		return thiz->get_operator_numberic(thiz);
	}
	return NULL;
}

int client_setup_data_connection(DongleClient* thiz, const char* apn, const char*user, const char* pwd)
{
#ifdef DONGLE_LOG
	ALOGD("%s %d thiz:%p", __func__, __LINE__, thiz );
#endif
	if(thiz != NULL && thiz->setup_data_connection != NULL)
	{
		return thiz->setup_data_connection(thiz, apn, user,pwd);
	}

	return 1;
}

static char* normal_dongle_get_operator_numeric(DongleClient* thiz)
{
	if(thiz->numeric == NULL) 
	{
		ATResponse *p_response = NULL;
		ATChannel* channel = NULL;

		char* at_path = dongle_get_port_path(thiz->owner, modem_info_fix_port_index(thiz->modem, 1));
		if(at_path == NULL)
		{
#ifdef DONGLE_LOG
			ALOGE("%s %d dongle_get_port_path failed", __func__, __LINE__);
#endif
			return NULL;
		}
		channel = at_channel_create(PORT_TYPE_SERIAL, at_path, NULL, NULL);
		if(at_send_command(channel, "ATE0", 5000, NULL) != 0) 
			goto err;
		if(at_send_command(channel, "AT+CFUN=1", 5000, NULL) != 0) 
			goto err;
		sleep(4);
		if(at_send_command(channel, "AT+CGMM", 5000, NULL) != 0)
			goto err;
		if(at_send_command(channel, "AT+CPIN?", 5000, NULL) != 0)
			goto err;

		if(at_command_exec_singleline_for_result(channel, "AT+CIMI", 5000, "", &p_response)) 
		{
			if(p_response->p_intermediates->line != NULL) 
			{
				thiz->numeric = strdup(p_response->p_intermediates->line);	
			}
		}
		at_response_free(p_response);
err:
		if(channel != NULL)
		{
			at_channel_destroy(channel);
		}
	}

	return thiz->numeric;
}

static int normal_dongle_setup_cdma_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd)
{
#ifdef DONGLE_LOG
	ALOGD("%s: apn=%s user=%s pwd=%s\n", __func__, apn, user, pwd);
#endif
	do
	{
		char* data_path = dongle_get_port_path(thiz->owner, modem_info_fix_port_index(thiz->modem, 0));
		if(data_path == NULL)
		{
#ifdef DONGLE_LOG
			ALOGE("%s %d dongle_get_port_path failed", __func__, __LINE__);
#endif
			break;
		}

		ppp_ctrl_start_pppd_with_call(thiz->ppp, data_path, user, pwd, DIAL_NUMBER_CDMA, 0);
	}while(0);

	return 0;
}

static int patch_for_normal_setup_data(DongleClient* thiz)
{
#ifdef DONGLE_LOG
    ALOGD("##############%s\n", __func__);
#endif
	ATChannel* channel = NULL;
	char* at_path = dongle_get_port_path(thiz->owner, modem_info_fix_port_index(thiz->modem, 1));
	if(at_path != NULL) 
	{
		channel = at_channel_create(PORT_TYPE_SERIAL, at_path, NULL, NULL);
		at_send_command(channel, "AT", 5000, NULL);
	}
	if(channel != NULL) 
	{
		at_channel_destroy(channel);
	}

	return 0;
}

static int normal_dongle_setup_gsm_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd)
{
	char cmd[256] = {0};
	ATChannel* channel = NULL;
	int err = 0;
#ifdef DONGLE_LOG
    ALOGD("##############%s: apn=%s user=%s pwd=%s\n", __func__, apn, user, pwd);
#endif
	do
	{
		char* data_path = dongle_get_port_path(thiz->owner, modem_info_fix_port_index(thiz->modem, 0));
		if(data_path == NULL)
		{
#ifdef DONGLE_LOG
			ALOGE("%s %d dongle_get_port_path failed", __func__, __LINE__);
#endif
			err = 1;
			break;
		}

		channel = at_channel_create(PORT_TYPE_SERIAL, data_path, NULL, NULL);
		at_send_command(channel, "ATH", 5000, NULL);

		snprintf(cmd, 128, "AT+CGDCONT=1,\"IP\",\"%s\",\"\",0,0", apn);
		if(!at_command_exec_for_result(channel, cmd, 5000))
		{
			patch_for_normal_setup_data(thiz);
			err = 1;
			break;
		}

		if(thiz->modem->network == GSM_MODE) {
			at_send_command(channel, "AT+CGQREQ=1", 5000, NULL);
			at_send_command(channel, "AT+CGQMIN=1", 5000, NULL);
			at_send_command(channel, "AT+CGACT=0,1", 5000, NULL);
		}
        at_send_command(channel, "AT+CPIN?",5000, NULL);
	
		if(thiz->modem->network == GSM_MODE)
		{
			snprintf(cmd, 128, "%s", DIAL_NUMBER_GSM);
		}
		else
		{
			snprintf(cmd, 128, "%s", DIAL_NUMBER_TDSCDMA);
		}

		if(!at_command_exec_for_result(channel, cmd, 10000))
		{
			err = 1;
			break;
		}
        ppp_ctrl_start_pppd(thiz->ppp, data_path, user, pwd,  0);
	}while(0);
	if(channel != NULL) 
	{
		at_channel_destroy(channel);
	}

	return err;
}

static int normal_dongle_setup_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd)
{
#ifdef DONGLE_LOG
	ALOGD("%s %d", __func__, __LINE__);
#endif
	if(thiz->modem->network == CDMA_MODE)
	{
		return normal_dongle_setup_cdma_data_connection(thiz, apn, user, pwd);
	}
	else
	{
		return normal_dongle_setup_gsm_data_connection(thiz, apn, user, pwd);
	}
}

DongleClient* dongle_client_create(Dongle* owner, PppCtrl* ppp, struct modem_info* info)
{
	DongleClient* thiz = NULL;
	if(ppp != NULL && info != NULL) 
	{
		if(check_is_dm_dongle_by_modem_info(info))
		{
			thiz = dm_dongle_client_create(ppp);
		}
		else
		{
			thiz = normal_dongle_client_create(ppp);
		}
		if(thiz != NULL)
		{
			thiz->owner = owner;
			client_set_modem_info(thiz, info);
		}
	}

	return thiz;
}

static DongleClient* dm_dongle_client_create(PppCtrl* ppp)
{
	DongleClient* thiz = calloc(1, sizeof(DongleClient));
	if(thiz != NULL)
	{
#ifdef DONGLE_LOG
		ALOGD("%s %d", __func__, __LINE__);
#endif
		thiz->get_operator_numberic = dm_dongle_get_operator_numeric;
		thiz->setup_data_connection = dm_dongle_setup_data_connection;
		thiz->ppp = ppp;
	}

	return thiz;
}

static int dm_dongle_setup_data_connection(DongleClient* thiz, const char* apn, const char* user, const char* pwd)
{	
	//FIXME zte dm dongle only support cdma now
	char* data_path = dongle_get_port_path(thiz->owner, modem_info_fix_port_index(thiz->modem, 0));
	if(data_path == NULL)
	{
#ifdef DONGLE_LOG
		ALOGE("%s %d dongle_get_port_path failed", __func__, __LINE__);
#endif
		return 1;
	}

	ppp_ctrl_start_pppd_with_call(thiz->ppp, data_path, user, pwd, DIAL_NUMBER_CDMA, 0);

	return 0;
}

static char* dm_dongle_get_operator_numeric(DongleClient* thiz)
{
	//numeric of zte dm dongle is constant
#ifdef DONGLE_LOG
	ALOGD("%s %d", __func__, __LINE__);
#endif
    
	return "404009542729847";
}

